package com.sh.rbac.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import com.sh.rbac.core.common.constant.CacheConstants;
import com.sh.rbac.core.common.constant.CommonConstants;
import com.sh.rbac.core.common.exception.CustomException;
import com.sh.rbac.core.common.util.IpAddressUtils;
import com.sh.rbac.core.common.util.JwtUtils;
import com.sh.rbac.core.common.util.WebUtils;
import com.sh.rbac.core.common.result.ResultCodeEnum;
import com.sh.rbac.core.security.entity.LoginUser;
import com.sh.rbac.core.security.util.SecurityHolderUtils;
import com.sh.rbac.domain.dto.auth.AccountLoginDTO;
import com.sh.rbac.domain.entity.User;
import com.sh.rbac.service.AuthService;
import com.sh.rbac.service.ParamService;
import com.sh.rbac.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 认证服务接口实现类
 *
 * @author wb
 * @since 2025-10-11
 */

@Slf4j
@Service
public class AuthServiceImpl implements AuthService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserService userService;

    @Autowired
    private ParamService paramService;

    @Override
    public Map<String, Object> accountLogin(AccountLoginDTO accountLoginDTO) {

        // 参数校验
        validateParams(accountLoginDTO);

        // 验证验证码
        validateCaptcha(accountLoginDTO.getCaptchaKey(), accountLoginDTO.getCaptcha());

        try {
            // 创建用户名密码令牌
            UsernamePasswordAuthenticationToken usernamePassword = new UsernamePasswordAuthenticationToken(accountLoginDTO.getUsername(), accountLoginDTO.getPassword());

            // 认证
            Authentication authenticate = authenticationManager.authenticate(usernamePassword);

            // 获取认证后的用户信息
            LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
            log.info("当前登录用户信息->{}", loginUser);

            // 获取用户信息
            User user = loginUser.getUser();
            Long userId = user.getId();

            // 生成 token
            Map<String, String> claimMap = new HashMap<>();
            claimMap.put("userId", userId.toString());

            String value = paramService.getParamValueByCode(CommonConstants.TOKEN_EXPIRE_TIME);
            if (value == null) {
                value = "900000";
            }
            long expireTime = Long.parseLong(value);
            String token = JwtUtils.createToken(claimMap, expireTime);

            user.setToken(token);
            user.setExpiration(System.currentTimeMillis() + expireTime);

            // 缓存登录用户信息
            redisTemplate.opsForValue().set(CacheConstants.LOGIN_USER_KEY + token, user, 30, TimeUnit.DAYS);

            // 返回登录结果
            Map<String, Object> result = new HashMap<>();
            result.put("userId", user.getId());
            result.put("token", token);
            result.put("username", user.getUsername());
            result.put("lastLoginTime", user.getLastLoginTime());

            return result;
        } catch (Exception e) {
            throw new CustomException(ResultCodeEnum.USERNAME_PASSWORD_ERROR);
        }
    }

    private void validateParams(AccountLoginDTO accountLoginDTO) {
        if (accountLoginDTO.getUsername() == null || accountLoginDTO.getUsername().isEmpty()) {
            throw new CustomException(ResultCodeEnum.USERNAME_NOT_NULL);
        }

        if (accountLoginDTO.getPassword() == null || accountLoginDTO.getPassword().isEmpty()) {
            throw new CustomException(ResultCodeEnum.PASSWORD_NOT_NULL);
        }
        if (accountLoginDTO.getCaptcha() == null || accountLoginDTO.getCaptcha().isEmpty()) {
            throw new CustomException(ResultCodeEnum.CAPTCHA_NOT_NULL);
        }
    }

    @Override
    public User getInfo() {
        User loginUser = SecurityHolderUtils.getLoginUser();

        if (loginUser == null) {
            throw new CustomException(ResultCodeEnum.AUTH_NOT_LOGIN);
        }

        // 更新用户登录信息
        updateLoginInfo(SecurityHolderUtils.getUserId());

        return loginUser;
    }

    @Override
    public Boolean logout() {
        String token = WebUtils.getToken();
        return redisTemplate.delete(CacheConstants.LOGIN_USER_KEY + token);
    }

    @Override
    public String captcha(String captchaKey) {
        // 定义图形验证码的长和宽
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(100, 40, CommonConstants.CAPTCHA_SIZE, 100);

        // 验证码值
        String code = lineCaptcha.getCode();

        // 将验证码缓存到 redis 中，1 分钟后失效
        redisTemplate.opsForValue().set(CacheConstants.CAPTCHA_KEY + captchaKey, code, CommonConstants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);

        // 验证码以base64的格式返回到客户端
        return lineCaptcha.getImageBase64Data();
    }


    /**
     * 验证验证码
     *
     * @param captchaKey 验证码 key
     * @param captcha    验证码
     */
    private void validateCaptcha(String captchaKey, String captcha) {
        String code = (String) redisTemplate.opsForValue().get(CacheConstants.CAPTCHA_KEY + captchaKey);

        if (code == null || code.isEmpty()) {
            throw new CustomException(ResultCodeEnum.CODE_NOT_FOUND);
        }

        if (!captcha.equalsIgnoreCase(code)) {
            throw new CustomException(ResultCodeEnum.CODE_ERROR);
        }

        redisTemplate.delete(CacheConstants.CAPTCHA_KEY + captchaKey);
    }

    /**
     * 更新登录信息
     *
     * @param id 用户ID
     */
    private void updateLoginInfo(Long id) {
        User user = new User();
        user.setId(id);
        user.setLastLoginIp(IpAddressUtils.getIpAddress(WebUtils.getRequest()));
        user.setLastLoginTime(LocalDateTime.now());

        userService.updateById(user);
    }
}
